package utils

import (
	"database/sql"
	"fmt"
	"net/url"
	"sync"

	"boomlive.com/boomlive-common/configs"
	"github.com/beego/beego/v2/client/orm"
	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

// DBConfig 定义数据库连接配置
// Alias: 唯一别名，用于标识该数据库连接
// Type: 数据库类型（mysql 或 mssql）
// User / Password / Host / Port / Name: 基础连接信息
// Charset: 字符集（仅 mysql 需要）
// MaxOpen / MaxIdle: 数据库连接池设置
type DBConfig struct {
	Alias    string
	Type     string // mysql or mssql
	User     string
	Password string
	Host     string
	Port     string
	Name     string
	Charset  string
	MaxOpen  int
	MaxIdle  int
}

var (
	dbClients     sync.Map // alias -> *sqlx.DB
	initOnceMap   sync.Map // alias -> *sync.Once
	ormRegisterMu sync.Mutex
)

// getDSN 返回 driver 名称和对应数据库的 DSN 字符串
func getDSN(cfg DBConfig) (string, string, error) {
	switch cfg.Type {
	case "mysql":
		return "mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=true",
			cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Name, cfg.Charset), nil
	case "mssql":
		userEscaped := url.QueryEscape(cfg.User)
		passwordEscaped := url.QueryEscape(cfg.Password)
		dsn := fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=%s&encrypt=disable",
			userEscaped, passwordEscaped, cfg.Host, cfg.Port, cfg.Name)
		return "sqlserver", dsn, nil
	default:
		return "", "", fmt.Errorf("不支持的数据库类型: %s", cfg.Type)
	}
}

// initDB 初始化数据库连接并返回 *sqlx.DB
func initDB(cfg DBConfig) (*sqlx.DB, error) {
	driver, dsn, err := getDSN(cfg)
	if err != nil {
		return nil, err
	}
	db, err := sql.Open(driver, dsn)
	if err != nil {
		return nil, fmt.Errorf("连接失败: %w", err)
	}
	if err := db.Ping(); err != nil {
		return nil, fmt.Errorf("Ping失败: %w", err)
	}
	db.SetMaxOpenConns(cfg.MaxOpen)
	db.SetMaxIdleConns(cfg.MaxIdle)
	return sqlx.NewDb(db, driver), nil
}

// RegisterDB 注册数据库连接（按需调用，幂等）
func RegisterDB(cfg DBConfig) (*sqlx.DB, error) {
	onceIface, _ := initOnceMap.LoadOrStore(cfg.Alias, &sync.Once{})
	once := onceIface.(*sync.Once)
	var initErr error
	once.Do(func() {
		db, err := initDB(cfg)
		if err != nil {
			initErr = err
			return
		}
		dbClients.Store(cfg.Alias, db)
	})
	if initErr != nil {
		return nil, initErr
	}
	val, ok := dbClients.Load(cfg.Alias)
	if !ok {
		return nil, fmt.Errorf("数据库未注册: %s", cfg.Alias)
	}
	return val.(*sqlx.DB), nil
}

// GetDB 获取已注册的数据库连接
func GetDB(alias string) (*sqlx.DB, error) {
	val, ok := dbClients.Load(alias)
	if !ok {
		return nil, fmt.Errorf("数据库连接不存在: %s", alias)
	}
	return val.(*sqlx.DB), nil
}

// RegisterBeegoORM 手动注册 ORM（Beego）用的数据库
func RegisterBeegoORM(alias string, db *sql.DB, driverName string) error {
	ormRegisterMu.Lock()
	defer ormRegisterMu.Unlock()
	if err := orm.RegisterDriver(driverName, orm.DRMySQL); err != nil {
		return fmt.Errorf("注册ORM驱动失败: %w", err)
	}
	if err := orm.AddAliasWthDB(alias, driverName, db); err != nil {
		return fmt.Errorf("注册ORM连接失败: %w", err)
	}
	return nil
}

//// ======================== mysql 数据库 mydb 初始化方法： ========================
//var (
//	MainCfg = DBConfig{
//		Alias:    configs.BoomliveDBAliasName,
//		Type:     configs.BoomliveDBDriver,
//		User:     configs.BoomliveDBUser,
//		Password: configs.BoomliveDBPwd,
//		Host:     configs.BoomliveDBHost,
//		Port:     configs.BoomliveDBPort,
//		Name:     configs.BoomliveDBName,
//		Charset:  configs.BoomliveDBCharset,
//		MaxOpen:  50,
//		MaxIdle:  20,
//	}
//)
//
//// 在业务逻辑中按需注册使用
//func InitMainDB() error {
//	_, err := RegisterDB(MainCfg)
//	return err
//}
//
//// 使用 ORM（备注：使用 ORM 时不用调 InitMainDB 了，InitMainORM 已经包含了）
//func InitMainORM() error {
//	err := InitMainDB()
//	if err != nil {
//		return err
//	}
//	db, err := GetDB(MainCfg.Alias)
//	if err != nil {
//		return err
//	}
//	return RegisterBeegoORM(MainCfg.Alias, db.DB, MainCfg.Type)
//}

// ======================== SQL Server 数据库 phonecast 初始化方法： ========================
var (
	UnicomCfg = DBConfig{
		Alias:    configs.UnicomDBAliasName,
		Type:     configs.UnicomDBDriver,
		User:     configs.UnicomDBUser,
		Password: configs.UnicomDBPwd,
		Host:     configs.UnicomDBHost,
		Port:     configs.UnicomDBPort,
		Name:     configs.UnicomDBName,
		Charset:  configs.UnicomDBCharset,
		MaxOpen:  50,
		MaxIdle:  20,
	}

	CoinDCfg = DBConfig{
		Alias:    configs.CoinDBAliasName,
		Type:     configs.CoinDBDriver,
		User:     configs.CoinDBUser,
		Password: configs.CoinDBPwd,
		Host:     configs.CoinDBHost,
		Port:     configs.CoinDBPort,
		Name:     configs.CoinDBName,
		Charset:  configs.CoinDBCharset,
		MaxOpen:  50,
		MaxIdle:  20,
	}
)

// 在业务逻辑中按需注册使用
func InitUnicomDB() error {
	_, err := RegisterDB(UnicomCfg)
	return err
}

func InitCoinDCfgDB() error {
	_, err := RegisterDB(CoinDCfg)
	return err
}

/* 使用示例：

// 假设 boomlive-openapi-game 项目中你需要如下连接：
// - cashew: mysql
// - stat:   mysql
// - admin:  mssql
// - jobs:   mssql

var (
	CashewCfg = DBConfig{
		Alias:    "cashew",
		Type:     "mysql",
		User:     configs.Cfg.DB.Cashew.User,
		Password: configs.Cfg.DB.Cashew.Password,
		Host:     configs.Cfg.DB.Cashew.Host,
		Port:     configs.Cfg.DB.Cashew.Port,
		Name:     configs.Cfg.DB.Cashew.Name,
		Charset:  "utf8mb4",
		MaxOpen:  50,
		MaxIdle:  20,
	}

	StatCfg = DBConfig{...} // 同上结构
	AdminCfg = DBConfig{...} // mssql 类型
	JobsCfg  = DBConfig{...} // mssql 类型
)

// 在业务逻辑中按需注册使用
func InitCashewDB() error {
	_, err := RegisterDB(CashewCfg)
	return err
}

func InitStatDB() error {
	_, err := RegisterDB(StatCfg)
	return err
}

// 使用 ORM（如果需要）
func InitCashewORM() error {
	db, err := GetDB("cashew")
	if err != nil {
		return err
	}
	return RegisterBeegoORM("cashew", db.DB, "mysql")
}
*/
